// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#pragma once

#include <map>
#include <string>
#include <unordered_map>
#include <fast_double_parser.h>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/stacktrace.hpp>
#include <rapidjson/document.h>

#include "coin/proto/coin_market_enums.pb.h"
#include "coin2/base/conversion.h"
#include "coin2/base/time.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/feed_util/book_builder.h"
#include "coin2/exchange/base/feed_util/trade.h"
#include "coin2/exchange/base/market/enums.h"

namespace impl {
using coin2::exchange::base::market::ExchangeType;
using coin2::exchange::base::market::MarketType;
using namespace coin::proto;

class BitstampFeedUpdateParser {
 public:
  static void ParseBook(BookBuilder* book_builder,
                const rapidjson::Value& data,
                bool *is_crossed_book) {
    int64_t timestamp = stoull(data["microtimestamp"].GetString()) * 1'000LL;
    auto tx = book_builder->NewTransaction(timestamp);
    tx.Clear();
    for (const auto& pq : data["asks"].GetArray()) {
      tx.UpdateAsk(
            fast_double_parser::stod(pq[0].GetString()),
            fast_double_parser::stod(pq[1].GetString()));
    }

    for (const auto& pq : data["bids"].GetArray()) {
      tx.UpdateBid(
            fast_double_parser::stod(pq[0].GetString()),
            fast_double_parser::stod(pq[1].GetString()));
    }

    *is_crossed_book = tx.IsCrossedBook();
  }

  static void ParseTrade(const rapidjson::Value& trade_json, Trade* trade) {
    coin2::exchange::feed::TradeSide side = ParseTradeSide(trade_json["type"].GetString());
    trade->side = side;
    // macro second-> ns
    trade->timestamp = stoull(trade_json["microtimestamp"].GetString()) * 1'000LL;
    trade->price = fast_double_parser::stod(trade_json["price_str"].GetString());
    trade->fill_qty = fast_double_parser::stod(trade_json["amount_str"].GetString());
  }

  static coin2::exchange::feed::TradeSide ParseTradeSide(const std::string& trade_type) {
    if (trade_type == "0") {
      return coin2::exchange::feed::TRADE_BUY_SIDE;
    } else if (trade_type == "1") {
      return coin2::exchange::feed::TRADE_SELL_SIDE;
    } else {
      throw std::runtime_error("[Bitstamp] unknown side: " + trade_type);
      return coin2::exchange::feed::TRADE_NO_SIDE;
    }
  }
};

}  // namespace impl

using impl::BitstampFeedUpdateParser;
